home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / fmt / fmt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-19  |  9.2 KB  |  441 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms are permitted
  6.  * provided that this notice is preserved and that due credit is given
  7.  * to the University of California at Berkeley. The name of the University
  8.  * may not be used to endorse or promote products derived from this
  9.  * software without specific prior written permission. This software
  10.  * is provided ``as is'' without express or implied warranty.
  11.  */
  12.  
  13. #ifndef lint
  14. char copyright[] =
  15. "@(#) Copyright (c) 1980 Regents of the University of California.\n\
  16.  All rights reserved.\n";
  17. #endif /* not lint */
  18.  
  19. #ifndef lint
  20. static char sccsid[] = "@(#)fmt.c    5.5 (Berkeley) 2/18/88";
  21. #endif /* not lint */
  22.  
  23. #include <stdio.h>
  24. #include <ctype.h>
  25.  
  26. /*
  27.  * fmt -- format the concatenation of input files or standard input
  28.  * onto standard output.  Designed for use with Mail ~|
  29.  *
  30.  * Syntax : fmt [ goal [ max ] ] [ name ... ]
  31.  * Authors: Kurt Shoens (UCB) 12/7/78;
  32.  *          Liz Allen (UMCP) 2/24/83 [Addition of goal length concept].
  33.  */
  34.  
  35. /* LIZ@UOM 6/18/85 -- Don't need LENGTH any more.
  36.  * #define    LENGTH    72        Max line length in output
  37.  */
  38. #define    NOSTR    ((char *) 0)    /* Null string pointer for lint */
  39.  
  40. /* LIZ@UOM 6/18/85 --New variables goal_length and max_length */
  41. int    goal_length = 65;    /* Target or goal line length in output */
  42. int    max_length = 75;    /* Max line length in output */
  43. int    pfx;            /* Current leading blank count */
  44. int    lineno;            /* Current input line */
  45. int    mark;            /* Last place we saw a head line */
  46.  
  47. char    *malloc();        /* for lint . . . */
  48. char    *headnames[] = {"To", "Subject", "Cc", 0};
  49.  
  50. /*
  51.  * Drive the whole formatter by managing input files.  Also,
  52.  * cause initialization of the output stuff and flush it out
  53.  * at the end.
  54.  */
  55.  
  56. main(argc, argv)
  57.     int argc;
  58.     char **argv;
  59. {
  60.     register FILE *fi;
  61.     register int errs = 0;
  62.     int number;        /* LIZ@UOM 6/18/85 */
  63.  
  64.     setout();
  65.     lineno = 1;
  66.     mark = -10;
  67.     /*
  68.      * LIZ@UOM 6/18/85 -- Check for goal and max length arguments 
  69.      */
  70.     if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
  71.         argv++;
  72.         argc--;
  73.         goal_length = number;
  74.         if (argc > 1 && (1 == (sscanf(argv[1], "%d", &number)))) {
  75.             argv++;
  76.             argc--;
  77.             max_length = number;
  78.         }
  79.     }
  80.     if (max_length <= goal_length) {
  81.         fprintf(stderr, "Max length must be greater than %s\n",
  82.             "goal length");
  83.         exit(1);
  84.     }
  85.     if (argc < 2) {
  86.         fmt(stdin);
  87.         oflush();
  88.         exit(0);
  89.     }
  90.     while (--argc) {
  91.         if ((fi = fopen(*++argv, "r")) == NULL) {
  92.             perror(*argv);
  93.             errs++;
  94.             continue;
  95.         }
  96.         fmt(fi);
  97.         fclose(fi);
  98.     }
  99.     oflush();
  100.     exit(errs);
  101. }
  102.  
  103. /*
  104.  * Read up characters from the passed input file, forming lines,
  105.  * doing ^H processing, expanding tabs, stripping trailing blanks,
  106.  * and sending each line down for analysis.
  107.  */
  108. fmt(fi)
  109.     FILE *fi;
  110. {
  111.     char linebuf[BUFSIZ], canonb[BUFSIZ];
  112.     register char *cp, *cp2;
  113.     register int c, col;
  114.  
  115.     c = getc(fi);
  116.     while (c != EOF) {
  117.         /*
  118.          * Collect a line, doing ^H processing.
  119.          * Leave tabs for now.
  120.          */
  121.         cp = linebuf;
  122.         while (c != '\n' && c != EOF && cp-linebuf < BUFSIZ-1) {
  123.             if (c == '\b') {
  124.                 if (cp > linebuf)
  125.                     cp--;
  126.                 c = getc(fi);
  127.                 continue;
  128.             }
  129.             if ((c < ' ' || c >= 0177) && c != '\t') {
  130.                 c = getc(fi);
  131.                 continue;
  132.             }
  133.             *cp++ = c;
  134.             c = getc(fi);
  135.         }
  136.         *cp = '\0';
  137.  
  138.         /*
  139.          * Toss anything remaining on the input line.
  140.          */
  141.         while (c != '\n' && c != EOF)
  142.             c = getc(fi);
  143.         
  144.         /*
  145.          * Expand tabs on the way to canonb.
  146.          */
  147.         col = 0;
  148.         cp = linebuf;
  149.         cp2 = canonb;
  150.         while (c = *cp++) {
  151.             if (c != '\t') {
  152.                 col++;
  153.                 if (cp2-canonb < BUFSIZ-1)
  154.                     *cp2++ = c;
  155.                 continue;
  156.             }
  157.             do {
  158.                 if (cp2-canonb < BUFSIZ-1)
  159.                     *cp2++ = ' ';
  160.                 col++;
  161.             } while ((col & 07) != 0);
  162.         }
  163.  
  164.         /*
  165.          * Swipe trailing blanks from the line.
  166.          */
  167.         for (cp2--; cp2 >= canonb && *cp2 == ' '; cp2--)
  168.             ;
  169.         *++cp2 = '\0';
  170.         prefix(canonb);
  171.         if (c != EOF)
  172.             c = getc(fi);
  173.     }
  174. }
  175.  
  176. /*
  177.  * Take a line devoid of tabs and other garbage and determine its
  178.  * blank prefix.  If the indent changes, call for a linebreak.
  179.  * If the input line is blank, echo the blank line on the output.
  180.  * Finally, if the line minus the prefix is a mail header, try to keep
  181.  * it on a line by itself.
  182.  */
  183. prefix(line)
  184.     char line[];
  185. {
  186.     register char *cp, **hp;
  187.     register int np, h;
  188.  
  189.     if (strlen(line) == 0) {
  190.         oflush();
  191.         putchar('\n');
  192.         return;
  193.     }
  194.     for (cp = line; *cp == ' '; cp++)
  195.         ;
  196.     np = cp - line;
  197.  
  198.     /*
  199.      * The following horrible expression attempts to avoid linebreaks
  200.      * when the indent changes due to a paragraph.
  201.      */
  202.     if (np != pfx && (np > pfx || abs(pfx-np) > 8))
  203.         oflush();
  204.     if (h = ishead(cp))
  205.         oflush(), mark = lineno;
  206.     if (lineno - mark < 3 && lineno - mark > 0)
  207.         for (hp = &headnames[0]; *hp != (char *) 0; hp++)
  208.             if (ispref(*hp, cp)) {
  209.                 h = 1;
  210.                 oflush();
  211.                 break;
  212.             }
  213.     if (!h && (h = (*cp == '.')))
  214.         oflush();
  215.     pfx = np;
  216.     split(cp);
  217.     if (h)
  218.         oflush();
  219.     lineno++;
  220. }
  221.  
  222. /*
  223.  * Split up the passed line into output "words" which are
  224.  * maximal strings of non-blanks with the blank separation
  225.  * attached at the end.  Pass these words along to the output
  226.  * line packer.
  227.  */
  228. split(line)
  229.     char line[];
  230. {
  231.     register char *cp, *cp2;
  232.     char word[BUFSIZ];
  233.     int wordl;        /* LIZ@UOM 6/18/85 */
  234.  
  235.     cp = line;
  236.     while (*cp) {
  237.         cp2 = word;
  238.         wordl = 0;    /* LIZ@UOM 6/18/85 */
  239.  
  240.         /*
  241.          * Collect a 'word,' allowing it to contain escaped white
  242.          * space. 
  243.          */
  244.         while (*cp && *cp != ' ') {
  245.             if (*cp == '\\' && isspace(cp[1]))
  246.                 *cp2++ = *cp++;
  247.             *cp2++ = *cp++;
  248.             wordl++;/* LIZ@UOM 6/18/85 */
  249.         }
  250.  
  251.         /*
  252.          * Guarantee a space at end of line. Two spaces after end of
  253.          * sentence punctuation. 
  254.          */
  255.         if (*cp == '\0') {
  256.             *cp2++ = ' ';
  257.             if (strchr(".:!", cp[-1]))
  258.                 *cp2++ = ' ';
  259.         }
  260.         while (*cp == ' ')
  261.             *cp2++ = *cp++;
  262.         *cp2 = '\0';
  263.         /*
  264.          * LIZ@UOM 6/18/85 pack(word); 
  265.          */
  266.         pack(word, wordl);
  267.     }
  268. }
  269.  
  270. /*
  271.  * Output section.
  272.  * Build up line images from the words passed in.  Prefix
  273.  * each line with correct number of blanks.  The buffer "outbuf"
  274.  * contains the current partial line image, including prefixed blanks.
  275.  * "outp" points to the next available space therein.  When outp is NOSTR,
  276.  * there ain't nothing in there yet.  At the bottom of this whole mess,
  277.  * leading tabs are reinserted.
  278.  */
  279. char    outbuf[BUFSIZ];            /* Sandbagged output line image */
  280. char    *outp;                /* Pointer in above */
  281.  
  282. /*
  283.  * Initialize the output section.
  284.  */
  285. setout()
  286. {
  287.     outp = NOSTR;
  288. }
  289.  
  290. /*
  291.  * Pack a word onto the output line.  If this is the beginning of
  292.  * the line, push on the appropriately-sized string of blanks first.
  293.  * If the word won't fit on the current line, flush and begin a new
  294.  * line.  If the word is too long to fit all by itself on a line,
  295.  * just give it its own and hope for the best.
  296.  *
  297.  * LIZ@UOM 6/18/85 -- If the new word will fit in at less than the
  298.  *    goal length, take it.  If not, then check to see if the line
  299.  *    will be over the max length; if so put the word on the next
  300.  *    line.  If not, check to see if the line will be closer to the
  301.  *    goal length with or without the word and take it or put it on
  302.  *    the next line accordingly.
  303.  */
  304.  
  305. /*
  306.  * LIZ@UOM 6/18/85 -- pass in the length of the word as well
  307.  * pack(word)
  308.  *    char word[];
  309.  */
  310. pack(word,wl)
  311.     char word[];
  312.     int wl;
  313. {
  314.     register char *cp;
  315.     register int s, t;
  316.  
  317.     if (outp == NOSTR)
  318.         leadin();
  319.     /*
  320.      * LIZ@UOM 6/18/85 -- change condition to check goal_length; s is the
  321.      * length of the line before the word is added; t is now the length
  322.      * of the line after the word is added
  323.      *    t = strlen(word);
  324.      *    if (t+s <= LENGTH) 
  325.      */
  326.     s = outp - outbuf;
  327.     t = wl + s;
  328.     if ((t <= goal_length) ||
  329.         ((t <= max_length) && (t - goal_length <= goal_length - s))) {
  330.         /*
  331.          * In like flint! 
  332.          */
  333.         for (cp = word; *cp; *outp++ = *cp++);
  334.         return;
  335.     }
  336.     if (s > pfx) {
  337.         oflush();
  338.         leadin();
  339.     }
  340.     for (cp = word; *cp; *outp++ = *cp++);
  341. }
  342.  
  343. /*
  344.  * If there is anything on the current output line, send it on
  345.  * its way.  Set outp to NOSTR to indicate the absence of the current
  346.  * line prefix.
  347.  */
  348. oflush()
  349. {
  350.     if (outp == NOSTR)
  351.         return;
  352.     *outp = '\0';
  353.     tabulate(outbuf);
  354.     outp = NOSTR;
  355. }
  356.  
  357. /*
  358.  * Take the passed line buffer, insert leading tabs where possible, and
  359.  * output on standard output (finally).
  360.  */
  361. tabulate(line)
  362.     char line[];
  363. {
  364.     register char *cp, *cp2;
  365.     register int b, t;
  366.  
  367.     /*
  368.      * Toss trailing blanks in the output line.
  369.      */
  370.     cp = line + strlen(line) - 1;
  371.     while (cp >= line && *cp == ' ')
  372.         cp--;
  373.     *++cp = '\0';
  374.     
  375.     /*
  376.      * Count the leading blank space and tabulate.
  377.      */
  378.     for (cp = line; *cp == ' '; cp++)
  379.         ;
  380.     b = cp-line;
  381.     t = b >> 3;
  382.     b &= 07;
  383.     if (t > 0)
  384.         do
  385.             putc('\t', stdout);
  386.         while (--t);
  387.     if (b > 0)
  388.         do
  389.             putc(' ', stdout);
  390.         while (--b);
  391.     while (*cp)
  392.         putc(*cp++, stdout);
  393.     putc('\n', stdout);
  394. }
  395.  
  396. /*
  397.  * Initialize the output line with the appropriate number of
  398.  * leading blanks.
  399.  */
  400. leadin()
  401. {
  402.     register int b;
  403.     register char *cp;
  404.  
  405.     for (b = 0, cp = outbuf; b < pfx; b++)
  406.         *cp++ = ' ';
  407.     outp = cp;
  408. }
  409.  
  410. /*
  411.  * Save a string in dynamic space.
  412.  * This little goodie is needed for
  413.  * a headline detector in head.c
  414.  */
  415. char *
  416. savestr(str)
  417.     char str[];
  418. {
  419.     register char *top;
  420.  
  421.     top = malloc(strlen(str) + 1);
  422.     if (top == NOSTR) {
  423.         fprintf(stderr, "fmt:  Ran out of memory\n");
  424.         exit(1);
  425.     }
  426.     strcpy(top, str);
  427.     return (top);
  428. }
  429.  
  430. /*
  431.  * Is s1 a prefix of s2??
  432.  */
  433. ispref(s1, s2)
  434.     register char *s1, *s2;
  435. {
  436.  
  437.     while (*s1++ == *s2)
  438.         ;
  439.     return (*s1 == '\0');
  440. }
  441.